exception Requete_en_cours (* Une requête à la fois *)
exception Pas_de_requete
let noms_egaux str1 str2 =
  (* On compare jusqu'au premier zéro *)
  let rec tester i =
    if i < String.length str1 && i < String.length str2 then
      if str1.[i] = char_of_int 0 && str2.[i] = char_of_int 0 then true
      else if str1.[i] = str2.[i] then tester (i + 1)
      else false
    else true
  in
  tester 0

type reponse =
  | Accepte
  | Refuse
  | Erreur (* le serveur n'a pas bien répondu *)
type joueur =
    {
      mutable connecte: bool;
      mutable nom: string;
      mutable dispo: string list;
      mutable noms_joueurs: string array;
      mutable identification_demandee: string option;
      mutable invitation_demandee: string list option;
      mutable chaine_reponse: (reponse -> unit) list;
      mutable en_jeu: bool;
    }
let est_connecte joueur = joueur.connecte
let string_of_joueur joueur = joueur.nom
let joueurs_disponibles joueur = joueur.dispo
let mes_adversaires joueur = Array.copy joueur.noms_joueurs
let joue joueur = joueur.en_jeu
let make () =
  {
    connecte = false;
    nom = "";
    dispo = [];
    noms_joueurs = Array.init 5 (fun _ -> "");
    identification_demandee = None;
    invitation_demandee = None;
    chaine_reponse = [];
    en_jeu = false;
  }
let requete_en_cours joueur = 
  match joueur.identification_demandee, 
	joueur.invitation_demandee with
  | None, None ->
     false
  | Some id, _ ->
     (* let () = Printf.printf "Je suis en train d'attendre mon invitation.\n%!" in *)
     true
  | _, Some [a ; b ; c ; d] ->
     (* let () = Printf.printf "Je suis en train d'inviter %s, %s, %s et %s.\n%!" *)
     (* 			    a b c d *)
     (* in *)
     true
  | _ ->
     (* let () = Printf.printf *)
     (* 		"Je suis en train d'inviter des gens...\n%!" in *)
     true
let traiter_reponse joueur f =
  if requete_en_cours joueur then
    joueur.chaine_reponse <- (f) :: joueur.chaine_reponse
  else raise Pas_de_requete
let repondre_aux joueur rep =
  List.iter (fun f -> f rep) (List.rev joueur.chaine_reponse);
  joueur.chaine_reponse <- [];
  joueur.identification_demandee <- None;
  joueur.invitation_demandee <- None
let requete_refusee joueur =
  repondre_aux joueur Refuse
let requete_acceptee joueur =
  repondre_aux joueur Accepte
let assimiler_requete_deconnecter joueur = 
  repondre_aux joueur Erreur;
  joueur.connecte <- false
let assimiler_requete_identifier joueur nom =
  if requete_en_cours joueur then 
    raise Requete_en_cours
  else
    joueur.identification_demandee <- Some nom
let assimiler_requete_inviter joueur noms =
  if requete_en_cours joueur then 
    raise Requete_en_cours
  else
    joueur.invitation_demandee <- Some noms
let assimiler_information_erreur joueur =
  repondre_aux joueur Refuse
let assimiler_information_connexion joueur =
  joueur.connecte <- true
let assimiler_information_deconnexion joueur =
  joueur.connecte <- false;
  repondre_aux joueur Erreur
let assimiler_information_entree joueur nom =
  joueur.dispo <- nom :: joueur.dispo;
  match joueur.identification_demandee with
  | Some n when noms_egaux n nom -> 
     let () = joueur.nom <- n in
     requete_acceptee joueur
  | _ -> ()
let assimiler_information_sortie joueur nom =
  joueur.dispo <- List.filter (fun n -> not (noms_egaux nom n))
			      joueur.dispo
(* Est-ce que ça répond à mon invitation ? *)
let invitation_reussie_aux noms requete mon_nom =
  let rec present element = function
    | [] -> false
    | hd :: _ when hd = element -> true
    | _ :: tl -> present element tl
  in
  let rec difference liste1 liste2 =
    match liste1 with
    | [] -> []
    | hd :: tl when not (present hd liste2) ->
       hd :: (difference tl liste2)
    | _ :: tl -> difference tl liste2
  in
  let listes_egales l1 l2 =
    difference l1 l2 = []
    && difference l2 l1 = []
  in
  listes_egales (Array.to_list noms)
		(mon_nom :: requete)
let assimiler_information_noms joueur noms =
  joueur.dispo <- [];
  joueur.noms_joueurs <- noms;
  joueur.en_jeu <- true;
  match joueur.invitation_demandee with
  | Some liste 
       when invitation_reussie_aux noms liste joueur.nom ->
     requete_acceptee joueur
  | Some _ ->
     requete_refusee joueur (* Entre-temps, on nous a invité *)
  | None -> ()
